package cn.open.feishu.alert.support;

import cn.open.feishu.alert.common.enums.AlertModeEnum;
import cn.open.feishu.alert.common.enums.AlertTypeEnum;
import cn.open.feishu.alert.common.enums.UserModeEnum;
import cn.open.feishu.alert.common.model.*;
import cn.open.feishu.alert.common.param.CardDirectAlertParam;
import cn.open.feishu.alert.common.utils.HttpUtil;
import cn.open.feishu.alert.common.utils.Md5Util;
import cn.open.feishu.alert.config.FeiShuProperties;
import cn.open.feishu.alert.context.SpringContext;
import cn.open.feishu.alert.support.Handler.FeishuDirectAlertHandler;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.text.MessageFormat;
import java.util.*;

import static cn.open.feishu.alert.common.FeishuConstant.*;
import static cn.open.feishu.alert.common.FeishuConstant.INTERVAL_MILL_SECONDS;

/**
 * @Description 飞书消息告警客户端
 * @Author fandy.lin
 * @Date 2021/11/22 10:43 上午
 **/
@Data
@Slf4j
@Builder
public class FeishuAlertClient {
    private FeiShuProperties feiShuProperties;
    private AccessToken accessToken;
    private volatile List<String> userIds;
    private volatile String userIdsCode;

    @Async("sendAsyncExecutor")
    public void sendAsync(String robotName, String alertText, AlertTypeEnum alertType) {
        this.send(robotName, alertText, alertType);
    }

    @Async("sendAsyncExecutor")
    public void sendAsync(CardDirectAlertParam param) {
        this.send(param);
    }

    /**
     * 即时:通过email直接发送告警文本给指定的人
     * @param param
     */
    public void send(CardDirectAlertParam param) {
        if(CollectionUtils.isEmpty(param.getEmails())){
            return;
        }
        SpringContext.getBean(FeishuDirectAlertHandler.class).send(param);
    }

    /**
     * 根据配置条件相应mode的handler进行告警,无配置则使用默认方式
     *
     * @param robotName
     * @param alertText
     * @param alertType
     */
    public void send(String robotName, String alertText, AlertTypeEnum alertType) {
        AlertModeEnum alertMode = AlertModeEnum.context.get(feiShuProperties.getAlertMode());
        if (alertMode == null) {
            alertMode = AlertModeEnum.DEFALUE;
        }
        alertMode.handleSend(robotName, alertText, alertType);
    }

    /**
     * 刷新访问飞书开放接口token
     *
     * @throws IOException
     */
    private void refreshToken() throws IOException {
        synchronized (this) {
            if (accessToken == null || accessToken.expireTimestamp - System.currentTimeMillis() < INTERVAL_MILL_SECONDS) {
                TokenRequest tokenReq = TokenRequest.builder().app_id(feiShuProperties.getAppId()).app_secret(feiShuProperties.getAppSecret()).build();
                TokenResponse tokenResp = HttpUtil.execute(feiShuProperties.getTokenRefreshUrl(), null, tokenReq, TokenResponse.class);
                if (tokenResp.getCode() != SUCCESS_CODE) {
                    log.warn("feishu alert:get access token not success,ret code={},msg={}", tokenResp.getCode(), tokenResp.getMsg());
                    return;
                }
                long expireTimestamp = System.currentTimeMillis() + tokenResp.getExpire() * 1000;
                Map<String, String> tokenMap = Maps.newHashMapWithExpectedSize(8);
                tokenMap.put("Authorization", MessageFormat.format("Bearer {0}", tokenResp.getTenant_access_token()));
                this.accessToken = new AccessToken(tokenMap, expireTimestamp);
                log.info("feishu alert:refresh access token={}", accessToken);
            }
        }
    }

    /**
     * 得到可访问飞书开放接口token
     *
     * @throws IOException
     */
    public Map<String, String> getTokenMap() {
        if (Objects.isNull(feiShuProperties.getAppId()) || Objects.isNull(feiShuProperties.getAppSecret())) {
            return null;
        }
        try {
            if (accessToken == null || accessToken.expireTimestamp - System.currentTimeMillis() < INTERVAL_MILL_SECONDS) {
                refreshToken();
            }
            return this.accessToken.tokenMap;
        } catch (IOException e) {
            log.error("feishu alert:get access token have error!", e);
        }
        return null;
    }

    /**
     * 刷新飞书告警配置owner的用户ID
     *
     * @param infos
     * @param userMode
     */
    public void refreshUser(String infos, UserModeEnum userMode) {
        Map<String, String> tokenMap = getUserEnable(infos);
        if (Objects.isNull(tokenMap)) {
            return;
        }
        try {
            String[] infoArr = infos.split(",");
            UserRequest userReq = userMode == UserModeEnum.EMAILS ? UserRequest.builder().emails(infoArr).build() :
                    UserRequest.builder().mobiles(infoArr).build();
            UserResponse userResp = HttpUtil.execute(feiShuProperties.getUserGainUrl(), tokenMap, userReq, UserResponse.class);
            if (userResp.getCode() != SUCCESS_CODE) {
                log.warn("feishu alert:get access token not success,resp={},req={}", userResp, userReq);
            }
            userIds = new ArrayList<>(8);
            List<UserResponse.UserId> userList = userResp.getData().getUser_list();
            for (UserResponse.UserId userId : userList) {
                if (Objects.nonNull(userId.getUser_id())) {
                    userIds.add(userId.getUser_id());
                }
            }
            userIdsCode = Md5Util.getEncode(infos);
            log.info("feishu alert:refresh user ids={}", JSON.toJSONString(userIds));
        } catch (IOException e) {
            log.error("feishu alert:get user info error!", e);
        }
    }


    public Map<String,String> findUserMap(List<String> emails) {
        try {
            Map<String, String> tokenMap = getTokenMap();
            UserRequest userReq =  UserRequest.builder().emails(emails.toArray(new String[emails.size()])).build();
            UserResponse userResp = HttpUtil.execute(feiShuProperties.getUserGainUrl(), tokenMap, userReq, UserResponse.class);
            if (userResp.getCode() != SUCCESS_CODE) {
                log.warn("feishu alert:get access token not success,resp={},req={}", userResp, userReq);
            }
            Map<String, String> userMap = Maps.newHashMapWithExpectedSize(emails.size());
            List<UserResponse.UserId> userList = userResp.getData().getUser_list();
            for (int i=0;i < userList.size();i++) {
                if (Objects.nonNull(userList.get(i).getUser_id())) {
                    userMap.put(emails.get(i),userList.get(i).getUser_id());
                }
            }
            log.info("feishu alert:find emails={} to user ids={}", JSON.toJSONString(emails),JSON.toJSONString(userList));
            return userMap;
        } catch (IOException e) {
            log.error("feishu alert:get user info error!", e);
        }
        return null;
    }

    private Map<String, String> getUserEnable(String infos) {
        if (StringUtils.isEmpty(infos)) {
            log.info("feishu alert:user set null");
            userIds = null;
            return null;
        }
        if(Md5Util.getEncode(infos).equals(userIdsCode)){
            log.info("feishu alert:user set same");
            return null;
        }
        Map<String, String> tokenMap = getTokenMap();
        if (Objects.isNull(tokenMap)) {
            log.warn("feishu alert:token map is null!");
            return null;
        }
        return tokenMap;
    }

    /**
     * 判断告警配置的owner相应用户Id是否获取成功
     *
     * @return
     */
    public Boolean existUser() {
        //1、用户Id已经存在则返回
        if (!CollectionUtils.isEmpty(userIds)) {
            return true;
        }
        //2、用户Id不存在,根据配置的owner去获取
        UserModeEnum userMode = null;
        String infos = null;
        if (Objects.nonNull(feiShuProperties.getOwnerEmails())) {
            userMode = UserModeEnum.EMAILS;
            infos = feiShuProperties.getOwnerEmails();
        }
        if (Objects.nonNull(feiShuProperties.getOwnerMobiles())) {
            userMode = UserModeEnum.MOBILES;
            infos = feiShuProperties.getOwnerMobiles();
        }
        if (Objects.isNull(userMode)) {
            return false;
        }
        refreshUser(infos, userMode);
        if (CollectionUtils.isEmpty(userIds)) {
            return false;
        }
        return true;
    }

    @AllArgsConstructor
    private class AccessToken {
        /**
         * 访问token
         */
        private Map<String, String> tokenMap;
        /**
         * 当前token的过期时间戳
         */
        private Long expireTimestamp;
    }
}
